#!/bin/bash
# 数据库自适应备份脚本
# 主要功能对innobackupex做了二次封装，一个脚本实现对整个服务器的全量和增量备份
# 通过crontab调度，可以每小时或者每隔一小时执行，默认在凌晨4点做全备，其他时段
# 做增量备份。
# 脚本采用流的方式推送备份文件到远端服务器，所以需要打通服务器互信
# 当然脚本也获取了当前的星期/月/日，可以自行做其他策略，比如每周一次全备，则只
# 需要在全备的判断中增加对于周的检测

export PATH=/usr/sbin:/usr/bin:/bin:/sbin:${PATH}

# 显示使用帮助
usage () {
	echo "Usage: ${0} [OPTION...]"
	echo "  -H, --help          This option displays a help screen and exits."
	echo "  -n, --compress-threads=#"
	echo "                      This option specifies the number of worker threads that"
	echo "                      will be used for parallel compression. It is passed"
	echo "                      directly to the xtrabackup child process."
	echo "  -r, --parallel=#    On backup, this option specifies the number of threads"
	echo "                      the xtrabackup child process should use to back up files"
	echo "                      concurrently.  The option accepts an integer argument. It"
	echo "                      is passed directly to xtrabackup's --parallel option. See"
	echo "                      the xtrabackup documentation for details."
	echo "  -u, --user=name     This option specifies the MySQL username used when"
	echo "                      connecting to the server, if that's not the current user."
	echo "                      The option accepts a string argument. See mysql --help"
	echo "                      for details."
	echo "  -h, --host=name     This option specifies the host to use when connecting to"
	echo "                      the database server with TCP/IP.  The option accepts a"
	echo "                      string argument. See mysql --help for details."
	echo "  -P, --port=#        This option specifies the port to use when connecting to"
	echo "                      the database server with TCP/IP.  The option accepts a"
	echo "                      string argument. See mysql --help for details."
	echo "  -p, --password=name This option specifies the password to use when connecting"
	echo "                      to the database. It accepts a string argument.  See mysql"
	echo "                      --help for details."
	echo "  -S, --socket=name   This option specifies the socket to use when connecting"
	echo "                      to the local database server with a UNIX domain socket."
	echo "                      The option accepts a string argument. See mysql --help"
	echo "                      for details."
	echo "  -l, --local-dir     The local backup directory for temp use"
	echo "  -R, --remote-server The remote server with SSH where you want to put the backup"
	echo "                      file into."
	echo "  -D, --remote-dir    The backup directory on remote server"
	echo "  -v, --version       Output version information and exit."
}

# 读取命令后参数
OPTS=`getopt -o Hvh:P:u:p:S:n:r:R:l:D:a: --long help,version,host:,port:,user:,password:,socket:,compress-threads:,parallel:,remote-server:,local-dir:,remote-dir:,at: -n 'parse-options' -- "$@"`

if [ $? != 0 ]
then
	exit 1 
fi

eval set -- "$OPTS"

# 参数默认值设定
HELP=0
VERSION=0
MYSQL_HOST='127.0.0.1'
MYSQL_PORT=3306
MYSQL_USER='backup'
MYSQL_PASS='changeme'
MYSQL_SOCK=''
INCREMENTAL=0
COMPRESS_THREADS=16
PARALLEL=16
STREAM='xbstream'
REMOTE_SERVER=''
LOCAL_DIR=''
REMOTE_DIR=''
AT='H4'

# 参数赋值
while true
do
	case "$1" in
		-H | --help                ) HELP=1;                 break   ;; # 显示帮助信息，无需解析更多参数直接退出
		-v | --version             ) VERSION=1;              break   ;; # 显示版本信息，无需解析更多参数直接退出
		-h | --host                ) MYSQL_HOST=$2;          shift 2 ;; # 备份的主机，默认localhost
		-P | --port                ) MYSQL_PORT=$2;          shift 2 ;; # 服务端口，默认3306
		-u | --user                ) MYSQL_USER=$2;          shift 2 ;; # 登录用户，默认backup
		-p | --password            ) MYSQL_PASS=$2;          shift 2 ;; # 登录密码
		-S | --socket              ) MYSQL_SOCK=$2;          shift 2 ;; # 嵌套字文件位置
		-n | --compress-threads    ) COMPRESS_THREADS=$2;    shift 2 ;; # 压缩线程数，默认开启压缩
		-r | --parallel            ) PARALLEL=$2;            shift 2 ;; # 并发子进程数量
		-R | --remote-server       ) REMOTE_SERVER=$2;       shift 2 ;; # 远程服务器信息，比如root@10.0.0.1
		-l | --local-dir           ) LOCAL_DIR=$2;           shift 2 ;; # 本地工作目录，默认/var/tmp
		-D | --remote-dir          ) REMOTE_DIR=$2;          shift 2 ;; # 远程备份路径，比如/data/backup
		-a | --at                  ) AT=$2;                  shift 2 ;; #

		-- ) shift; break ;;
		* ) break ;;
	esac
done

# 显示版本
if [[ $VERSION -eq 1 ]]
then
	echo "MySQL Adaptive Backup v1.0.0"
	exit 0
fi

# 显示帮助
if [[ $HELP -eq 1 ]]
then
	usage
	exit 0
fi

# 对参数进行判断，如果没有提供则报错并退出
if ! [ -n "${REMOTE_SERVER}" ]
then
	echo "Please specify the action you want to run with -R or --remote-server"
	exit 1
fi

if ! [ -n "${LOCAL_DIR}" ]
then
	LOCAL_DIR="/data/backup_${MYSQL_PORT}"
fi

if ! [ -n "${REMOTE_DIR}" ]
then
	echo "Please specify the action you want to run with -D or --remote-dir"
	exit 1
fi

# 如果本地目录不存在，则创建
if ! [ -d "${LOCAL_DIR}" ]
then
        mkdir -p ${LOCAL_DIR}
fi

# 开始备份
h=$(date +"%k")                # 当前的小时数，24小时制
d=$(date +"%-d")               # 当前日期的天，可以用于更复杂的备份策略
m=$(date +"%-m")               # 当前日期的月，可以用于更复杂的备份策略
w=$(date +"%u")                # 当前日期的周，可以用于更复杂的备份策略
t=$(date +"%Y_%m_%d_%H_%M")    # 备份文件名上的时间戳
lock="/var/tmp/backup_${MYSQL_PORT}.lock"
log="/var/tmp/backup_${MYSQL_PORT}.log"

# 判断有效日期，暂时没用
:<<!
case ${M} in
	[13578]|10|12) days=31 ;;
	[469]|11)      days=30 ;;
	*)             days=29 ;;
ecas
!

eval $(echo ${AT} | grep -oP '[A-Z]\d+' | sed -nr 's/([MDWH])([[:digit:]]+)/\1=\2/p')

SCHEDULE=(${m} ${d} ${w} 4)

if [ -n "${M}" ]
then
	if [[ ${M} -ge 1 && ${M} -le 12  ]]
	then
		SCHEDULE[0]=${M}
	fi
fi
if [ -n "${D}" ]
then
	if [[ ${D} -ge 1 && ${D} -le 31 ]]
	then
		SCHEDULE[1]=${D}
	fi
fi
if [ -n "${W}" ]
then
	if [[ ${W} -ge 1 && ${W} -le 7 ]]
	then
		SCHEDULE[2]=${W}
	fi
fi
if [ -n "${H}" ]
then
	if [[ ${H} -ge 0 && ${H} -le 23 ]]
	then
		SCHEDULE[3]=${H}
	fi
fi

# 如果文件锁存在，说明上次备份未完成或者异常结束
if [ -f "${lock}" ]
then
	echo "[`date +'%Y-%m-%d %H:%M:%S'`] ----------------------------------------" >> ${log}
	echo "[`date +'%Y-%m-%d %H:%M:%S'`] Another backup is already in progress. You must complete that backup before continuing this one." >> ${log}
	exit 1
fi

# 临时目录
tmpdir="/tmp/backup_${MYSQL_PORT}"
if [ -d "${tmpdir}" ]
then
	rm -rf "${tmpdir}"
fi
mkdir "${tmpdir}"

# 创建一个文件锁
touch ${lock}

echo "[`date +'%Y-%m-%d %H:%M:%S'`] ----------------------------------------" >> ${log}

# 生成备份命令
CMD="innobackupex --compress"
CMD="${CMD} --host=${MYSQL_HOST}"
CMD="${CMD} --port=${MYSQL_PORT}"
CMD="${CMD} --user=${MYSQL_USER}"
CMD="${CMD} --password=${MYSQL_PASS}"
CMD="${CMD} --compress-threads=${COMPRESS_THREADS}"
CMD="${CMD} --parallel=${PARALLEL}"
CMD="${CMD} --extra-lsndir=${LOCAL_DIR}"
CMD="${CMD} --stream=xbstream"
CMD="${CMD} --tmpdir=${tmpdir}"

# 判断是否满足全备的条件
if [[ ${m} -eq ${SCHEDULE[0]} ]] && [[ ${d} -eq ${SCHEDULE[1]} ]] && [[ ${w} -eq ${SCHEDULE[2]} ]] && [[ ${h} -eq ${SCHEDULE[3]} ]]
then
	echo "[`date +'%Y-%m-%d %H:%M:%S'`] Make full backup" >> ${log}
else
	echo "[`date +'%Y-%m-%d %H:%M:%S'`] Make incremental backup" >> ${log}
	CMD="${CMD} --incremental"
	CMD="${CMD} --incremental-basedir=${LOCAL_DIR}"
fi

CMD="${CMD} ${LOCAL_DIR}"
#ssh ${REMOTE_SERVER} "cat - > ${REMOTE_DIR}_${t}.xbs" <<@
#$($CMD)
#@
echo "[`date +'%Y-%m-%d %H:%M:%S'`] ${CMD} 2>> ${log} | ssh ${REMOTE_SERVER} \"cat - > ${REMOTE_DIR}_${t}.xbs\"" >> ${log}
echo "<!--Original InnobackupEx log-->" >> ${log}
echo "////////////////////////////////////////////////////////////////////////////////" >> ${log}

# 执行备份操作
${CMD} 2>> ${log} | ssh ${REMOTE_SERVER} "cat - > ${REMOTE_DIR}_${t}.xbs"

echo "////////////////////////////////////////////////////////////////////////////////" >> ${log}
echo "[`date +'%Y-%m-%d %H:%M:%S'`] " >> ${log}

rc=$?
if [[ ${rc} -eq 0 ]]
then
	echo "[`date +'%Y-%m-%d %H:%M:%S'`] Backup completed" >> ${log}
else
	echo "[`date +'%Y-%m-%d %H:%M:%S'`] An error occurred during backup" >> ${log}
fi

# 备份完成移除文件锁
if [ -f "${lock}" ]
then
	rm -rf ${lock}
fi

exit ${rc}

